//--------------------------------------------------------------------------------------
// Constant Buffers
//--------------------------------------------------------------------------------------

cbuffer cbPerFrame : register( b0 )
{
	matrix g_mWorld;                   // World matrix for object
	matrix g_mView;					 // View matrix for object
	matrix g_mProj;					 // View matrix for object
	/*
	float3 g_vEyePos;
	
	float3 g_vLightPos[3];
	float4 g_vLightColor[3];

	float4 g_Ambient;
	float4 g_Diffuse;
	float4 g_Specular;
	float g_kDiffuse;
	float g_PhongExp;
	int   g_NumLights;
	int   dummy;*/
};

Texture2D txDiffuse : register( t0 );
SamplerState samLinear : register( s0 );

//------------------------------------------------------------
struct VS_INPUT2
{
	float3 vPosition	:POSITION;
	float2 vTex			:TEXCOORD0;
};

struct VS_OUTPUT
{
	float3 vPosition	:WORLDPOS;
	float2 vTex			:TEXCOORD0;
};
struct HS_OUTPUT
{
    float3 vPosition	: BEZIERPOS;
	float2 vTex			:TEXCOORD0;
};
struct DS_OUTPUT
{
	float4 vPosition	: POSITION;
	float3 v3DPos		: WORLDPOS;
	float2 vTex			:TEXCOORD0;
	float3 vNormal		:NORMAL;
	float4 vColor		:COLOR0;
};
struct GS_OUTPUT
{
	float4 vPosition	: SV_POSITION;
	float4 vColor		:COLOR0;
	float3 v3DPos		:WORLDPOS;
	float3 vNormal		:NORMAL;
	float2 vTex			:TEXCOORD0;
};
struct PS_INPUT
{
	float4 vPosition	: SV_POSITION;
	float4 vColor		:COLOR0;
	float2 vTex			:TEXCOORD0;
};

float4 PhongModel(float4 Pos, float3 Normal)
{
	float3 g_vEyePos = float3(3.0,3.0,3.5);
	int g_NumLights = 3;
	float3 g_vLightPos[3] = {float3(0, 6, 0),float3(10, 0, 0),float3(-10, -10, 0)};
	float4 g_vLightColor[3] = {float4(0.6, 0.6, 0.6, 1),float4(0, 0.2, 0.7, 1),float4(1, 0.3, 0, 1)};
	
	float4 g_Ambient  = float4(0.3, 0.3, 0.3, 1);
	float4 g_Diffuse  = float4(0.7, 0.7, 0.7, 1);
	float4 g_Specular = float4(0.65, 0.65, 0.25, 1);
	float g_kDiffuse = 1.0f;
	float g_PhongExp = 128.0f;
	
	float4 Color = g_Ambient;
	float3 EyeToPos = normalize(Pos - g_vEyePos);
	float3 LightToPos = float3(0,0,0), Reflected = float3(0,0,0);
	float4 LightPos = float4(0.0,0.0,0.0,1.0);
	float DiffuseIntensity = 0, Spec = 0;
	
	for (int i = 0; i < g_NumLights; i++)
	{
		LightPos = float4(g_vLightPos[i], 1);
		LightToPos = normalize(Pos - LightPos);
		// Compute the diffuse component
		DiffuseIntensity = saturate(dot(-LightToPos, Normal));
		// Compute the specular component
		Reflected = normalize(LightToPos - 2*dot(Normal, LightToPos) * Normal);
		Spec = saturate(dot(-Reflected, EyeToPos));
		Spec = pow(max(Spec, 0), g_PhongExp);
		Color = Color + g_vLightColor[i] * ((DiffuseIntensity * g_Diffuse) + (Spec * g_Specular));
	}

	return Color;
}

//--------------------------------------------------------------------------
//Vertex shader
//--------------------------------------------------------------------------
VS_OUTPUT SmoothVS(VS_INPUT2 In)
{	
	VS_OUTPUT result;
    //result.vPosition=mul(float4(In.vPosition, 1.0f), g_mWorld);
	result.vPosition = In.vPosition;
	result.vTex = In.vTex;
    return result;  
}


//----------------------------------------------------------------------------------
//	Basic Hull Shader
//
//----------------------------------------------------------------------------------

struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[4]			: SV_TessFactor;
    float Inside[2]			: SV_InsideTessFactor;
};


HS_CONSTANT_DATA_OUTPUT MyConstantsHS( InputPatch<VS_OUTPUT, 4> ip,
                                                 uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;
    
    float TessAmount = 16.0f;
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
   
    return Output;
}


//----------------------------------------------------------------------------------
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("MyConstantsHS")]
HS_OUTPUT SmoothHS( InputPatch<VS_OUTPUT, 4> p, 
                                     uint i : SV_OutputControlPointID,
                                     uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vPosition=p[i].vPosition;
	Output.vTex=p[i].vTex;
    return Output;
}


//--------------------------------------------------------------------
//Domain shader
//--------------------------------------------------------------------

[domain("quad")]
DS_OUTPUT SmoothDS( HS_CONSTANT_DATA_OUTPUT input, 
                        float2 UV : SV_DomainLocation,
                        const OutputPatch<HS_OUTPUT, 4> bezpatch )
{

    DS_OUTPUT Output;
		
	float phi   = bezpatch[3].vPosition.x*UV.x; //3.14159265358979323846*UV.x; 		
	float theta = bezpatch[3].vPosition.y*UV.y; //2*3.14159265358979323846*UV.y;
	float r;
	//r = 1;
	//r = 1-cos(theta)*sin(phi);
	r = 2*sin(theta*2)*sin(phi*2);
	//r = 2/sin(phi);
	
	float3 WorldPos=float3(0.0,0.0,0.0);
	
	WorldPos.x = r*cos(theta)*sin(phi);
	WorldPos.y = r*sin(theta)*sin(phi);
	WorldPos.z = r*cos(phi);
	
	Output.v3DPos = mul(float4(WorldPos,1.0f), g_mWorld);
	Output.vPosition = mul(float4(Output.v3DPos,1.0f), g_mView);
	Output.vTex.x = UV.x;
	Output.vTex.y = UV.y;
	
	float h = 0.0002441;
	//float h = 0.000001;
	
	float dr,dt,dp;
	float f11,f12,f21,f22;
	
	/*
	f11 = 1.0;
	f12 = 1.0;
	f21 = 1.0;
	f22 = 1.0;
	dr = -1.0;
	*/
	
	f11 = 2*sin((theta+h))*sin(phi);
	f12 = 2*sin((theta-h))*sin(phi);
	f21 = 2*sin(theta)*sin((phi+h));
	f22 = 2*sin(theta)*sin((phi-h));
	//dr = 2*sin(theta)*sin((phi))/abs(2*sin(theta)*sin((phi)));
	
	dr = -1.0;
	
	/*
	f11 = 1-cos(theta+h)*sin(phi);
	f12 = 1-cos(theta-h)*sin(phi);
	f21 = 1-cos(theta)*sin(phi+h);
	f22 = 1-cos(theta)*sin(phi-h);
	//dr = (1-cos(theta)*sin(phi))/abs(1-cos(theta)*sin(phi));
	dr = 1.0;
	*/
	dt = (f11-f12)/(2*h);
	dp = (f21-f22)/(2*h);
	
	if( UV.x==0.0 || UV.x==1.0 )
	{
		Output.vNormal = -normalize(	float3(	((cos(theta)*cos(phi))/r)*dp,
											((sin(theta)*cos(phi))/r)*dp,
											cos(phi)*dr
									  ));
	}
	else
	{
	Output.vNormal = -normalize(	float3(	(cos(theta)*sin(phi)*dr - (sin(theta)/(r*sin(phi)))*dt + ((cos(theta)*cos(phi))/r)*dp),
										(sin(theta)*sin(phi)*dr + (cos(theta)/(r*sin(phi)))*dt + ((sin(theta)*cos(phi))/r)*dp),
										cos(phi)*dr - (sin(phi)/r)*dp
									  ));
	}
	
	//float3 gradient = float3( -1.0 , 1/r * dp, 1.0/(r*sin(phi)) * dt);
	
	//Output.vNormal = normalize(float3( gradient.x*cos(gradient.z)*sin(gradient.y),dr*sin(gradient.z)*sin(gradient.y),dr*cos(gradient.y)));
	
	Output.vColor = float4(0.9,0.9,0.9,1.0);
	
	Output.vNormal = mul(float4(Output.vNormal,1.0f), g_mWorld);
	
	return Output;    
}


//--------------------------------------------------------------------
//Geometry shader
//--------------------------------------------------------------------


//---------------------------------------------------------------------------

[maxvertexcount(3)]
void SmoothGS( triangle DS_OUTPUT input[3], inout TriangleStream<GS_OUTPUT> TriStream )
{	
    
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( -faceNormal );
    float4 color = float4(0.9,0.9,0.9,1.0);
	
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vPosition = input[0].vPosition;
	vP0.v3DPos =input[0].v3DPos;
	//vP0.vNormal=faceNormal;
	vP0.vNormal = input[0].vNormal;
	vP0.vColor =color;
	//vP0.vColor = input[0].vColor;
	vP0.vTex = input[0].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[1].vPosition;
	vP0.v3DPos =input[1].v3DPos;
	//vP0.vNormal=faceNormal;
	vP0.vNormal = input[1].vNormal;
	vP0.vColor =color;
	//vP0.vColor = input[1].vColor;
	vP0.vTex = input[1].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[2].vPosition;
    vP0.v3DPos =input[2].v3DPos;
    //vP0.vNormal=faceNormal;
	vP0.vNormal = input[2].vNormal;
	vP0.vColor =color;
	//vP0.vColor = input[2].vColor;
	vP0.vTex = input[2].vTex;
	TriStream.Append( vP0 );
	
	TriStream.RestartStrip();
	
}

[maxvertexcount(6)]
void SmoothGSGrass( triangle DS_OUTPUT input[3], inout LineStream<GS_OUTPUT> LnStream )
{	
    
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( -faceNormal );
    float grassH = 0.3;
	float4 color = float4(0.3,0.9,0.7,1.0);
	float4 tmp;
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vColor=color;
    vP0.vPosition = input[0].vPosition;
	vP0.vColor =color;
	vP0.v3DPos =input[0].v3DPos;
	vP0.vNormal=input[0].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[0].v3DPos+input[0].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
    
    vP0.vPosition = input[1].vPosition;
	vP0.vColor =color;
	vP0.v3DPos =input[1].v3DPos;
	vP0.vNormal=input[1].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[1].v3DPos+input[1].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
	
    LnStream.RestartStrip();
    
    vP0.vPosition = input[2].vPosition;
    vP0.vColor =color;
    vP0.v3DPos =input[2].v3DPos;
	vP0.vNormal=input[2].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[2].v3DPos+input[2].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
}

//--------------------------------------------------------------------
//Geometry shader
//--------------------------------------------------------------------

//---------------------------------------------

float4 SmoothPS(GS_OUTPUT In) : SV_TARGET
{
	return In.vColor*PhongModel(float4(In.v3DPos, 1.0),In.vNormal);
}

float4 SmoothPS2(GS_OUTPUT In) : SV_TARGET
{
	return In.vColor;
}

